package com.eastmoney.emis.edda.infrastructure.hsbc.converter;

import com.eastmoney.emis.edda.config.HsbcEddaConfig;
import com.eastmoney.emis.edda.infrastructure.encrypt.Encryption;
import com.eastmoney.emis.edda.infrastructure.hsbc.HsbcEddaApi;
import com.eastmoney.emis.edda.infrastructure.hsbc.annotation.HsbcEncrypt;
import com.eastmoney.emis.edda.infrastructure.hsbc.dto.HsbcEddaBaseResp;
import com.eastmoney.emis.edda.projection.HsbcEddaContext;
import com.eastmoney.emis.logger.LoggerFactory;
import org.slf4j.Logger;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.util.FileCopyUtils;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Base64;

/**
 * @description: Description
 * @projectName: EDDA-HSBC
 * @package: com.eastmoney.emis.edda.infrastructure.converter
 * @author: 汪程程
 * @date: 2022/9/27 10:23 AM
 * @version: 1.0
 */
public class HsbcEddaEncryptMessageConverter extends MappingJackson2HttpMessageConverter {

    private static Logger log = LoggerFactory.getLogger(HsbcEddaEncryptMessageConverter.class);
    private Encryption encryption;
    private HsbcEddaConfig eddaConfig;

    public HsbcEddaEncryptMessageConverter(Encryption encryption, HsbcEddaConfig config) {
        super();
        this.encryption = encryption;
        this.eddaConfig = config;
    }

    @Override
    protected boolean supports(Class<?> clazz) {
        return clazz.getAnnotation(HsbcEncrypt.class) != null;
    }

    @Override
    public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) {
        return type instanceof Class && supports((Class<?>) type);
    }

    @Override
    protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        pubConnectHeaders(outputMessage);
        byte[] data = this.getObjectMapper().writeValueAsBytes(object);
        // encrypt and sign
        EncryptRequest request = new EncryptRequest();
        request.RequestBase64 = Base64.getEncoder().encodeToString(encryption.signAndEncrypt(data));

        HsbcEddaContext context = HsbcEddaApi.CONTEXT.get();
        context.setReqBase64(request.RequestBase64);
        context.setReqPayload(new String(data));
        super.writeInternal(request, type, outputMessage);
    }

    private void pubConnectHeaders(HttpOutputMessage outputMessage) {
        HttpHeaders headers = outputMessage.getHeaders();
        headers.add("x-hsbc-profile-id", eddaConfig.getProfileId());
        headers.add("x-hsbc-client-id", eddaConfig.getClientId());
        headers.add("x-hsbc-client-secret", eddaConfig.getClientSecret());
        headers.add("x-hsbc-country-code", eddaConfig.getCountryCode());
    }

    @Override
    public HsbcEddaBaseResp read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        byte[] data = FileCopyUtils.copyToByteArray(inputMessage.getBody());
        EncryptResponse encryptResponse;
        HsbcEddaContext context = HsbcEddaApi.CONTEXT.get();
        try {
            encryptResponse = getObjectMapper().readValue(data, EncryptResponse.class);
            context.setRespBase64(encryptResponse.ResponseBase64);
            // 加密串为空
            if (encryptResponse.ResponseBase64 == null) {
                context.setRespPayload(new String(data));
                HsbcEddaBaseResp resp = (HsbcEddaBaseResp) getObjectMapper().readValue(data, (Class) type);
                resp.context(context);
                return resp;
            }
            byte[] plainText = encryption.verifyAndDecrypt(Base64.getDecoder().decode(encryptResponse.ResponseBase64));
            context.setRespPayload(new String(plainText));
            HsbcEddaBaseResp resp = (HsbcEddaBaseResp) getObjectMapper().readValue(plainText, (Class) type);
            resp.context(context);
            return resp;
        } catch (Exception e) {
            ClientHttpResponse response = (ClientHttpResponse) inputMessage;
            try {
                HsbcEddaBaseResp resp = (HsbcEddaBaseResp) ((Class) type).getDeclaredConstructor().newInstance();
                resp.context(context);
                context.setRespPayload(response.getStatusCode() + "," + new String(data));
                resp.setNetworkError(true);
                return resp;
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        } finally {
            HsbcEddaApi.CONTEXT.remove();
            log.info("context: req = {}, resp = {}", context.getReqPayload(), context.getRespPayload());
        }

    }


    static class EncryptRequest {
        public String RequestBase64;
    }

    static class EncryptResponse {
        public String ResponseBase64;
    }
}
